home *** CD-ROM | disk | FTP | other *** search
/ Aminet 8 / Aminet 8 (1995)(GTI - Schatztruhe)[!][Oct 1995].iso / Aminet / util / cli / iuw_clitools.lha / src / lines.c < prev    next >
C/C++ Source or Header  |  1995-09-05  |  13KB  |  486 lines

  1. /* lines - extract lines from a text file (head & tail in Unix)
  2.  *
  3.  * Copyright (C) 1995 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
  4.  *
  5.  * Permission to use, copy, modify, and distribute this software and its
  6.  * documentation for any purpose and without fee is hereby granted, provided
  7.  * that the above copyright notice appear in all copies and that both that
  8.  * copyright notice and this permission notice appear in supporting
  9.  * documentation.  This software is provided "as is" without express or
  10.  * implied warranty.
  11.  *
  12.  * V1.0: 16/Feb/95  first version
  13.  * V1.1: 19/Feb/95  minor bugfix
  14.  * V2.0: 06/Aug/95  major write speed-up
  15.  */
  16. #define THIS_PROGRAM    "lines"
  17. #define THIS_VERSION    "1.2"
  18.  
  19. #include <exec/types.h>
  20. #include <exec/libraries.h>
  21. #include <exec/memory.h>
  22. #include <dos/dos.h>
  23. #include <dos/rdargs.h>
  24. #include <dos/dosasl.h>
  25. /*#include <clib/alib_protos.h>*/
  26. #include <stdlib.h>
  27. #include <string.h>
  28.  
  29. extern __stkargs void * LibCreatePool(ULONG, ULONG, ULONG);
  30. extern __stkargs void   LibDeletePool(void *);
  31. extern __stkargs void * LibAllocPooled(void *, ULONG);
  32. extern __stkargs void   LibFreePooled(void *, void *, ULONG);
  33. extern void NewList(struct List *);
  34.  
  35. #define SysBase_DECLARED
  36. #include <proto/exec.h>
  37. #include <proto/dos.h>
  38.  
  39. extern struct Library *SysBase;
  40.  
  41.  
  42. #define BUFFER_BLOCK_SIZE   2*1024
  43. #define POOLSIZE            32*1024
  44. #define BREAKSIGS           SIGBREAKF_CTRL_C
  45.  
  46. static const char amiga_version[] = "\0$VER: " THIS_PROGRAM " " THIS_VERSION " (" __COMMODORE_DATE__ ")";
  47.  
  48. const char Template[] = "FIRST/A/N,LAST/A/N,FROM,TO";
  49. __aligned struct {
  50.     LONG    *first;
  51.     LONG    *last;
  52.     STRPTR  from;
  53.     STRPTR  to;
  54. } Args;
  55.  
  56. typedef struct {
  57.     enum {
  58.         FINISH = 0,
  59.         SKIPUNTIL,      /* skip lines until line # <num> */
  60.         COPYTO,         /* copy lines until (including) line # <num> */
  61.         FIFOCOPY,       /* store upto <num> lines in FIFO, copy excess lines to output */
  62.         FIFOSKIP,       /* store upto <num> lines in FIFO, throw excess lines away */
  63.     } state;
  64.     LONG num;
  65. } State;
  66.  
  67.  
  68. typedef struct {
  69.     LONG            refs;   /* how many LineSegments point to this block */
  70.     LONG            off;    /* offset of next unused byte in data */
  71.     LONG            len;    /* number of valid bytes in data */
  72.     UBYTE           data[BUFFER_BLOCK_SIZE];
  73. } Block;
  74.  
  75. typedef struct {
  76.     struct MinNode  node;
  77.     Block *         block;  /* block data belongs to */
  78.     UBYTE *         data;   /* points into 'data' part of block */
  79.     LONG            len;    /* length of segment */
  80. } LineSegment;
  81.  
  82. typedef struct {
  83.     Block *         block;  /* block data belongs to */
  84.     UBYTE *         data;   /* points into 'data' part of block */
  85.     LONG            len;    /* length of segment */
  86. } BlockSegment;
  87.  
  88. typedef struct {
  89.     struct MinNode  node;
  90.     struct MinList  segs;   /* list of LineSegments */
  91. } Line;
  92.  
  93. static LONG err = 0;        /* error code */
  94. static BOOL eof = FALSE;    /* end of file in input */
  95. static int lastout = -1;    /* last character written to output */
  96.  
  97.  
  98. /* prototypes */
  99. static void     FreeBlock(Block *);     /* decrement reference count, free if 0 */
  100. static Block *  GetBlock(BPTR);         /* get current block (reads from file) */
  101. static void     FreeLine(Line *);       /* free a line & all segments */
  102. static Line *   ReadLine(BPTR);         /* read a line */
  103. static void     WriteLine(Line *, BPTR);/* write a line */
  104. static void     WriteBlock(BlockSegment *, BPTR);
  105. static void     ReduceFifo(LONG, BPTR); /* reduce FIFO to <num> lines, write excess to fh (unless fh = 0) */
  106. static void     StoreLine(Line *);      /* store line in FIFO */
  107. static void *   palloc(ULONG);
  108. static void     pfree(void *);
  109.  
  110.  
  111. static APTR mempool;
  112.  
  113. static void *
  114. palloc(bytes)
  115.     ULONG bytes;
  116. {
  117.     ULONG *p;
  118.     if( p = (ULONG *)LibAllocPooled(mempool, bytes+sizeof(ULONG)) )
  119.         *p++ = bytes;
  120.     return (void *)p;
  121. }
  122.  
  123.  
  124. static void
  125. pfree(p)
  126.     void *p;
  127. {
  128.     p = (ULONG *)p-1;
  129.     LibFreePooled(mempool, p, *(ULONG *)p);
  130. }
  131.  
  132.  
  133. static void
  134. FreeBlock(block)
  135.     Block *block;
  136. {
  137.     if( --block->refs == 0 )
  138.         pfree(block);
  139. }
  140.  
  141.  
  142. static Block *
  143. GetBlock(fh)
  144.     BPTR fh;
  145. {
  146.     static Block *block = NULL;
  147.     LONG r;
  148.  
  149.     if( !block )
  150.         block = (Block *)palloc(sizeof(Block));
  151.     else
  152.     if( block->off >= block->len ) {
  153.         /* all data of block has been used, free & get new */
  154.         FreeBlock(block);
  155.         block = (Block *)palloc(sizeof(Block));
  156.     }
  157.     else
  158.         return block;   /* block has unused data */
  159.  
  160.     if( block ) {
  161.         r = Read(fh, block->data, BUFFER_BLOCK_SIZE);
  162.         if( r > 0 ) {
  163.             block->len = r;
  164.             block->off = 0;
  165.             block->refs = 1;        /* static block ptr */
  166.         }
  167.         else {
  168.             if( r == 0 )
  169.                 eof = TRUE;
  170.             else
  171.                 err = IoErr();
  172.             pfree(block);
  173.             block = NULL;
  174.         }
  175.     }
  176.     else
  177.         err = ERROR_NO_FREE_STORE;
  178.     return block;
  179. }
  180.  
  181.  
  182. static void
  183. FreeLine(line)
  184.     Line *line;
  185. {
  186.     LineSegment *seg;
  187.  
  188.     if( line ) {
  189.         while( seg = (LineSegment *)RemHead((struct List *)&(line->segs)) ) {
  190.             FreeBlock(seg->block);
  191.             pfree(seg);
  192.         }
  193.         pfree(line);
  194.     }
  195. }
  196.  
  197.  
  198. static Line *
  199. ReadLine(fh)
  200.     BPTR fh;
  201. {
  202.     Block *block;
  203.     LineSegment *seg;
  204.     Line *line;
  205.     LONG r, total;
  206.     BOOL found = FALSE;
  207.  
  208.     if( line = (Line *)palloc(sizeof(Line)) ) {
  209.         NewList((struct List *)&(line->segs));
  210.         total = 0;
  211.         while( !found && !err && !eof ) {
  212.             if( block = GetBlock(fh) ) {
  213.                 if( seg = (LineSegment *)palloc(sizeof(LineSegment)) ) {
  214.                     AddTail((struct List *)&(line->segs), (struct Node *)seg);
  215.                     seg->block = block;
  216.                     block->refs++;
  217.                     seg->data = &(block->data[block->off]);
  218.  
  219.                     /* find newline */
  220.                     for( r = block->off; !found && r < block->len; r++ ) {
  221.                         if( block->data[r] == '\n' )
  222.                             found = TRUE;
  223.                     }
  224.                     seg->len = r - block->off;
  225.                     total += seg->len;
  226.                     block->off = r;
  227.                 }
  228.                 else
  229.                     err = ERROR_NO_FREE_STORE;
  230.             }
  231.         }
  232.  
  233.         if( total == 0 ) {
  234.             FreeLine(line);
  235.             line = NULL;
  236.         }
  237.     }
  238.     else
  239.         err = ERROR_NO_FREE_STORE;
  240.  
  241.     return line;
  242. }
  243.  
  244.  
  245. static void
  246. WriteBlock(seg, fh)
  247.     BlockSegment *seg;
  248.     BPTR fh;
  249. {
  250.     LONG w;
  251.  
  252.     if( seg->block ) {
  253.         w = Write(fh, seg->data, seg->len);
  254.         if( w > 0 )
  255.             lastout = seg->data[w-1];
  256.         if( w != seg->len )
  257.             err = IoErr();
  258.         FreeBlock(seg->block);
  259.         seg->block = NULL;
  260.         seg->data = NULL;
  261.         seg->len = 0;
  262.     }
  263. }
  264.  
  265.  
  266. static void
  267. WriteLine(line, fh)
  268.     Line *line;
  269.     BPTR fh;
  270. {
  271.     static BlockSegment bseg;
  272.     LineSegment *seg;
  273.  
  274.     if( line ) {
  275.         for( seg = (LineSegment *)(line->segs.mlh_Head);
  276.              !err && seg->node.mln_Succ;
  277.              seg = (LineSegment *)(seg->node.mln_Succ) ) {
  278.  
  279.             if( seg->block != bseg.block ) {
  280.                 WriteBlock(&bseg, fh);
  281.                 bseg.data  = seg->data;
  282.                 bseg.block = seg->block;
  283.                 seg->block->refs++;
  284.             }
  285.             bseg.len += seg->len;
  286.         }
  287.     }
  288.     else
  289.         WriteBlock(&bseg, fh);
  290. }
  291.  
  292.  
  293. static struct MinList fifo;
  294. static LONG fifosize = 0;       /* number of entries in FIFO */
  295.  
  296. static void
  297. ReduceFifo(num, fh)
  298.     LONG num;
  299.     BPTR fh;
  300. {
  301.     Line *line;
  302.  
  303.     while( !err && fifosize > num ) {
  304.         line = (Line *)RemHead((struct List *)&fifo);
  305.         --fifosize;
  306.         if( fh )
  307.             WriteLine(line, fh);
  308.         FreeLine(line);
  309.     }
  310. }
  311.  
  312.  
  313. static void
  314. StoreLine(line)
  315.     Line *line;
  316. {
  317.     if( line ) {
  318.         AddTail((struct List *)&fifo, (struct Node *)line);
  319.         ++fifosize;
  320.     }
  321. }
  322.  
  323.  
  324.  
  325. void
  326. _main()
  327. {
  328.     struct RDArgs *rdargs;
  329.     BPTR infh, outfh;
  330.     BOOL closeinfh, closeoutfh;
  331.     LONG nextline;
  332.     State statelist[3], *state = &statelist[0];
  333.  
  334.     if( SysBase->lib_Version < 37 ) {
  335.         #define MESSAGE  THIS_PROGRAM " requires AmigaOS 2.04 or higher\n"
  336.         Write(Output(), MESSAGE, sizeof(MESSAGE)-1);
  337.         _exit(RETURN_FAIL);
  338.         #undef MESSAGE
  339.     }
  340.  
  341.     if( mempool = LibCreatePool(MEMF_ANY, POOLSIZE, POOLSIZE) ) {
  342.         if( rdargs = ReadArgs(Template, (LONG *)&Args, NULL) ) {
  343.  
  344.             NewList((struct List *)&fifo);
  345.  
  346.             if( Args.from ) {
  347.                 infh = Open(Args.from, MODE_OLDFILE);
  348.                 closeinfh = TRUE;
  349.             }
  350.             else {
  351.                 infh = Input();
  352.                 closeinfh = FALSE;
  353.             }
  354.             if( Args.to ) {
  355.                 outfh = Open(Args.to, MODE_NEWFILE);
  356.                 closeoutfh = TRUE;
  357.             }
  358.             else {
  359.                 outfh = Output();
  360.                 closeoutfh = FALSE;
  361.             }
  362.             if( !infh || !outfh ) {
  363.                 err = IoErr();
  364.                 goto fail;
  365.             }
  366.  
  367.             statelist[2].state = FINISH;
  368.             if( *Args.first > 0 ) {
  369.                 statelist[0].state = SKIPUNTIL; statelist[0].num = *Args.first;
  370.                 if( *Args.last < 0 ) {
  371.                     statelist[1].state = FIFOCOPY; statelist[1].num = -(*Args.last+1);
  372.                 }
  373.                 else
  374.                 if( *Args.last >= *Args.first ) {
  375.                     statelist[1].state = COPYTO; statelist[1].num = *Args.last;
  376.                 }
  377.                 else
  378.                     err = ERROR_BAD_NUMBER;
  379.             }
  380.             else
  381.             if( *Args.first < 0 ) {
  382.                 statelist[0].state = FIFOSKIP; statelist[0].num = -(*Args.first);
  383.                 if( *Args.last >= 0 )
  384.                     err = ERROR_BAD_NUMBER;
  385.                 else
  386.                 if( *Args.last >= *Args.first ) {
  387.                     statelist[1].state = FIFOCOPY; statelist[1].num = -(*Args.last+1);
  388.                 }
  389.                 else
  390.                     err = ERROR_BAD_NUMBER;
  391.             }
  392.             else    /* *Args.first == 0 */
  393.                 err = ERROR_BAD_NUMBER;
  394.  
  395.             if( err )
  396.                 goto fail;
  397.  
  398.             nextline = 1;
  399.  
  400.             while( !err && state->state != FINISH ) {
  401.                 Line *line;
  402.  
  403.                 if( CheckSignal(BREAKSIGS) != 0 )
  404.                     err = ERROR_BREAK;
  405.                 else
  406.                 switch( state->state ) {
  407.                     case SKIPUNTIL:
  408.                         if( eof || (nextline >= state->num) )
  409.                             ++state;
  410.                         else {
  411.                             line = ReadLine(infh); ++nextline;
  412.                             FreeLine(line);
  413.                         }
  414.                     break;
  415.                     case COPYTO:
  416.                         if( eof || (nextline > state->num) )
  417.                             ++state;
  418.                         else {
  419.                             line = ReadLine(infh); ++nextline;
  420.                             WriteLine(line, outfh);
  421.                             FreeLine(line);
  422.                         }
  423.                     break;
  424.                     case FIFOCOPY:
  425.                         ReduceFifo(state->num, outfh);
  426.                         if( eof )
  427.                             ++state;
  428.                         else {
  429.                             line =  ReadLine(infh); ++nextline;
  430.                             StoreLine(line);
  431.                         }
  432.                     break;
  433.                     case FIFOSKIP:
  434.                         if( eof )
  435.                             ++state;
  436.                         else {
  437.                             line = ReadLine(infh); ++nextline;
  438.                             StoreLine(line);
  439.                             ReduceFifo(state->num, (BPTR)0);
  440.                         }
  441.                     break;
  442.                 }
  443.             }
  444.  
  445.             WriteLine(NULL, outfh);     /* flush pending writes */
  446.             /* ReduceFifo(0, (BPTR)0); */
  447.  
  448.         fail:
  449.             if( infh && closeinfh )
  450.                 Close(infh);
  451.             if( outfh ) {
  452.                 if( IsInteractive(outfh) ) {
  453.                     if( lastout > 0  && lastout != '\n' )
  454.                         FPutC(outfh, '\n');
  455.                 }
  456.                 if( closeoutfh )
  457.                     Close(outfh);
  458.             }
  459.  
  460.             FreeArgs(rdargs);
  461.         }
  462.         else    /* error in arguments */
  463.             err = IoErr();
  464.  
  465.         LibDeletePool(mempool);
  466.     }
  467.     else    /* could not create mempool */
  468.         err = ERROR_NO_FREE_STORE;
  469.  
  470.     if( err ) {
  471.         char smallbuf[256];
  472.  
  473.         /* can't use PrintFault() because it prints to stdout */
  474.         if( Fault(err, THIS_PROGRAM, smallbuf, 256) ) {
  475.             if( outfh = Open("CONSOLE:", MODE_READWRITE) ) {
  476.                 FPuts(outfh, smallbuf);
  477.                 FPutC(outfh, '\n');
  478.                 Close(outfh);
  479.             }
  480.         }
  481.         SetIoErr(err);  /* restore for 'why' command */
  482.     }
  483.     _exit(err ? RETURN_ERROR : RETURN_OK);
  484. }
  485.  
  486.